#region File Description
//-----------------------------------------------------------------------------
// Screen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using TimeWar.Octree;

#endregion

namespace TimeWar
{
    /// <summary>
    /// Screen represents a unit of rendering for the game, generally transitional point such 
    /// as splash screens, selection screens and the actual game levels.
    /// </summary>
    abstract public class GameScreen : TimeWar.Screen
    {
        /// <summary>
        /// The root of the scene graph for this screen
        /// </summary>

        /// <summary>
        /// Overlay points to a screen that will be drawn AFTER this one, more than likely overlaying it
        /// </summary>
        protected GameScreen overlay;

        protected SpriteBatch batch = null;

        protected Game game = null;

        //protected IOctree<CRenderedObject> octree = null;
        protected CRenderedObjectOctree modelOctree = null;

        protected Camera gameCamera = null;
        protected ChaseCamera chaseCamera = null;
        protected CRenderedObject chasedObject = null;
        protected CameraTypes cameraType = CameraTypes.MapView;
        protected GameState screenState = GameState.None;

        protected int zoneCount = 0;

        protected GraphicsDeviceManager deviceManager = null;

        //protected Particles particles = null;
        //protected Octree<Particles> particlesOctree = null;
        protected ParticleOctree particlesOctree = null;

        public Game GameInstance
        {
            get
            {
                return game;
            }
        }

        public SpriteBatch SpriteBatch
        {
            get
            {
                return batch;
            }
        }

        public GameScreen(Game game, GraphicsDeviceManager pDeviceManager)
        {
            this.game = game;

            deviceManager = pDeviceManager;

            if (game != null)
            {
                IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)game.Services.GetService(typeof(IGraphicsDeviceService));
                batch = new SpriteBatch(graphicsService.GraphicsDevice);
            }

            //octree = new Octree<CRenderedObject>();
            modelOctree = new CRenderedObjectOctree();
            particlesOctree = new ParticleOctree();

            gameCamera = new SceneCamera();

            gameCamera.Position = new Vector3(0.0f, 5.0f, 5.0f);

            chaseCamera = new ChaseCamera();

            // Set camera perspective
            //chaseCamera.NearPlaneDistance = 0.2f;
            //chaseCamera.FarPlaneDistance = 500.0f;

            //setupCameras();
        }

        protected void setupCameras()
        {

            //chasedObject = getChasedObject();

            chaseCamera.Up = chasedObject.Up;
            chaseCamera.NearClip = 0.01f;
            chaseCamera.FarClip = 1000.0f;
            gameCamera.NearClip = 0.1f;
            gameCamera.FarClip = 1000.0f;

            //gameCamera.Update(chasedObject.Position, chasedObject.Rotation);
            //chaseCamera.Update(chasedObject.Position, chasedObject.Rotation);

        }

        protected void callObjectUpdate(TimeSpan time, TimeSpan elapsedTime)
        {
            if (null == modelOctree)
            {
                return;
            }

            Vector3 refObjForUpdatePos;

            if (null != chasedObject)
            {
                //callObjectUpdateByType(chasedObject, time, elapsedTime);
                refObjForUpdatePos = chasedObject.Position;
                //refObjForUpdatePos = Vector3.Zero;
            }
            else
            {
                refObjForUpdatePos = gameCamera.Position;
            }

            modelOctree.setRange(refObjForUpdatePos, 1000);
            modelOctree.Update(time, elapsedTime);


        }


        protected void callParticleUpdate(TimeSpan time, TimeSpan elapsedTime)
        {
            if (null == particlesOctree)
            {
                return;
            }

            Vector3 refObjForUpdatePos;

            if (null != chasedObject)
            {
                //callObjectUpdateByType(chasedObject, time, elapsedTime);
                refObjForUpdatePos = chasedObject.Position;
            }
            else
            {
                refObjForUpdatePos = gameCamera.Position;
            }

            //List<Particles> objects = particlesOctree.GetNodes(refObjForUpdatePos, 100);
            particlesOctree.setRange(refObjForUpdatePos, 100);
            particlesOctree.Update(time, elapsedTime);


        }

        protected abstract void callObjectUpdateByType(CRenderedObject obj, TimeSpan time, TimeSpan elapsedTime);

        /// <summary>
        /// Update changes the layout and positions based on input or other variables
        /// Base class updates all items in the scene then calls any overlays to get them to render themselves
        /// </summary>
        /// <param name="time">Total game time since it was started</param>
        /// <param name="elapsedTime">Elapsed game time since the last call to update</param>
        /// <returns>The next gamestate to transition to. Default is the return value of an overlay or NONE. Override Update if you want to change this behaviour</returns>
        public override GameState Update(TimeSpan time, TimeSpan elapsedTime)
        {
            if (null == chasedObject)
            {
                return GameState.None;
            }

            //Update objects within scene/screen
            callObjectUpdate(time, elapsedTime);
            callParticleUpdate(time, elapsedTime);

            gameCamera.Update(chasedObject.Position, chasedObject.Rotation);
            chaseCamera.Up = chasedObject.Up;
            chaseCamera.Update(chasedObject.Position, chasedObject.Rotation);

            //process camera views:
            if (XInputHelper.GamePads[PlayerIndex.One].APressed)
            {
                if (CameraTypes.MapView == cameraType)
                {
                    cameraType = CameraTypes.ChaseView;
                }
                else if (CameraTypes.ChaseView == cameraType)
                {
                    cameraType = CameraTypes.MapView;                    
                }

            }

            //checkForCollisions();

            //reset controllers
            resetControllers();

            //Default is no state changes, override the class if you want a different state
            if (overlay != null)
            {
                return overlay.Update(time, elapsedTime);
            }

            return screenState;
        }

        protected void resetControllers()
        {
            XInputHelper.GamePads[PlayerIndex.One].Update(game, Keyboard.GetState(PlayerIndex.One));
            XInputHelper.GamePads[PlayerIndex.Two].Update(game, Keyboard.GetState(PlayerIndex.Two));
            XInputHelper.GamePads[PlayerIndex.Three].Update(game, Keyboard.GetState(PlayerIndex.Three));
            XInputHelper.GamePads[PlayerIndex.Four].Update(game, Keyboard.GetState(PlayerIndex.Four));
        }

        protected void renderObjectsViaOctree()
        {

            if (null == modelOctree)
            {
                return;
            }

            Vector3 renderFrom;

            if (null != chasedObject && cameraType == CameraTypes.ChaseView)
            {
                //renderFrom = Vector3.Zero;
                renderFrom = chasedObject.Position;
            }
            else
            {
                renderFrom = gameCamera.Position;
            }

            modelOctree.setRange(renderFrom, 1000);

            Matrix cameraView = gameCamera.View;
            Matrix cameraProjection = gameCamera.Projection;
            Camera currCamera = gameCamera;

            if (cameraType == CameraTypes.ChaseView)
            {
                cameraView = chaseCamera.View;
                cameraProjection = chaseCamera.Projection;
                currCamera = chaseCamera;
            }

            modelOctree.setRenderParameters(currCamera.Frustum, cameraView, cameraProjection);
            //modelOctree.Render();
            modelOctree.Render(currCamera.Frustum, renderFrom, 1000, cameraView, cameraProjection);
             
        }

        protected void renderParticles()
        {
            if (null == particlesOctree)
            {
                return;
            }

            Vector3 renderFrom;

            if (null != chasedObject)
            {
                renderFrom = chasedObject.Position;
            }
            else
            {
                renderFrom = gameCamera.Position;
            }


            particlesOctree.setRange(renderFrom, 1000);

            Camera cameraObj = gameCamera;
            Matrix cameraView = gameCamera.View;
            Matrix cameraProjection = gameCamera.Projection;

            if (cameraType == CameraTypes.ChaseView)
            {
                cameraView = chaseCamera.View;
                cameraProjection = chaseCamera.Projection;
                cameraObj = chaseCamera;
            }

            particlesOctree.setRenderParameters(cameraObj.Frustum, cameraView, cameraProjection);
            particlesOctree.Render();


        }

        protected void objectRender()
        {
            //renderObjectsViaList();
            renderObjectsViaOctree();
            renderParticles();
        }


        /// <summary>
        /// Renders this scene. The base class renders everything in the sceen graph and then calls any overlays to 
        /// get them to render themselves
        /// </summary>
        public override void Render()
        {
            //deviceManager.GraphicsDevice.Clear(Color.Black);
            //Render this scene then any overlays
            //deviceManager.GraphicsDevice.Clear(Color.Black);
            //IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)GameInstance.Services.GetService(typeof(IGraphicsDeviceService));

            //GraphicsDevice device = graphicsService.GraphicsDevice;

            //Sprites will always be at the back.
            //device.RenderState.DepthBufferWriteEnable = false;
            //device.Clear(Color.Black);
            
            //deviceManager.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
            deviceManager.GraphicsDevice.RenderState.DepthBufferEnable = true;  //required for proper depth rendering

            //deviceManager.GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

            deviceManager.GraphicsDevice.Clear(Color.Black);
            

            objectRender();

            if (overlay != null)
                overlay.Render();
        }

        /// <summary>
        /// Tidies up the scene. Base class does nothing but calls the overlays 
        /// </summary>
        //public virtual void Shutdown()
        //{
        //    if (overlay != null)
        //        overlay.Shutdown();

        //    if (batch != null)
        //    {
        //        batch.Dispose();
        //        batch = null;
        //    }
        //}

        /// <summary>
        /// OnCreateDevice is called when the device is created
        /// </summary>
        public virtual void OnCreateDevice()
        {
            //Re-Create the Sprite Batch!
            IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)game.Services.GetService(typeof(IGraphicsDeviceService));
            batch = new SpriteBatch(graphicsService.GraphicsDevice);
        }

        protected CRenderedObject getChasedObject()
        {
            return chasedObject;
        }


        //protected void octreeCheckForCollisions(Vector3 screenPosition)
        //{
        //    //Only checks if objects near the player object has collided
        //    List<CRenderedObject> screenItems = octree.GetNodes(screenPosition, 500);

        //    if (null == screenItems || screenItems.Count < 2)
        //    {
        //        return;
        //    }

        //    for (int i = 0; i < screenItems.Count - 1; i++)
        //    {
        //        CRenderedObject objA = screenItems[i];

        //        for (int j = i + 1; j < screenItems.Count; j++)
        //        {
        //            CRenderedObject objB = screenItems[j];

        //            //check for collision and process
        //            bool collided = objA.hasCollidedWith(objB);

        //            ObjectTypes typeA = objA.ShapeType;
        //            ObjectTypes typeB = objB.ShapeType;

        //            if (typeA == ObjectTypes.Character || typeB == ObjectTypes.Character)
        //            {
        //                //check
        //                //typeB = typeB;
        //                if (typeA == ObjectTypes.Weapon)
        //                {
        //                    String x = game.ToString();
        //                }
        //                else if (typeB == ObjectTypes.Weapon)
        //                {
        //                    String x = game.ToString();
        //                }
        //            }

        //            if (collided)
        //            {
                                                
        //                //Particles particles = new Particles(GameInstance, deviceManager, "Textures\\circle", "Textures\\p2_dual", 2000);
        //                //particles.myPosition = objA.Position;
        //                //particlesOctree.AddNode(objA.Position, particles);
        //                //particles.Render();
        //                //particles.Explosion();


        //                if (objA.ShapeType == ObjectTypes.Weapon)
        //                {
        //                    if (objB.ShapeType == ObjectTypes.Starship)
        //                    {
        //                        onWeaponCollision(objA, objB);
        //                    }
        //                    else if (objB.ShapeType == ObjectTypes.Character)
        //                    {




        //                    }
        //                }
        //                else if (objB.ShapeType == ObjectTypes.Weapon)
        //                {
        //                    if (objA.ShapeType == ObjectTypes.Starship)
        //                    {
        //                        onWeaponCollision(objA, objB);

        //                        //if (((Weapon)objB).Player == ((Starship)objA).Player)
        //                        //{
        //                        //    //friendly fire?
        //                        //    currAudioCue = Sound.Play(Sounds.ExplodeShip);
        //                        //}
        //                        //else
        //                        //{
        //                        //    //game.Exit();
        //                        //    currAudioCue = Sound.Play(Sounds.HyperspaceActivate);
        //                        //}
        //                    }
        //                    else
        //                    {
        //                        //game.Exit();
        //                        //currAudioCue = Sound.Play(Sounds.RocketFire);
        //                    }

        //                }
        //                else
        //                {

        //                    //Process collision
        //                    processCollision((CRenderedObject)objA, (CRenderedObject)objB);
        //                    objA.Move(TimeSpan.FromSeconds(10));
        //                    objB.Move(TimeSpan.FromSeconds(10));

        //                }

        //            }
        //        }

        //    }


        //}

        //public void checkForCollisions()
        //{
        //    //orgCheckForCollisions();
        //    //int totalObjectsInScreen = 0;

        //    Vector3 screenSector;

        //    if (zoneCount > 3)
        //    {
        //        zoneCount = 0;
        //    }

        //    if (zoneCount == 0)
        //    {
        //        screenSector = Vector3.Zero;
        //    }
        //    else if (zoneCount == 1)
        //    {
        //        screenSector = new Vector3(1000f, 0, 0);
        //    }
        //    else if (zoneCount == 2)
        //    {
        //        screenSector = new Vector3(0, 1000f, 0);
        //    }
        //    else
        //    {
        //        screenSector = new Vector3(0, 0, 1000f);
        //    }

        //    //octreeCheckForCollisions(screenSector);

        //    zoneCount++;

        //}

        //protected void processCollision(CRenderedObject objA, CRenderedObject objB)
        //{
        //    objB.Velocity = objA.getAfterCollisionVelocity(objB);
        //}

        public void addScreenObject(CRenderedObject obj)
        {
            //octree.AddNode(obj.Position, obj);
            modelOctree.AddNode(obj);
        }

        public void addScreenParticles(Particles particleEffects)
        {
            particlesOctree.AddNode(particleEffects);
        }

        public abstract void onWeaponCollision(CRenderedObject objA, CRenderedObject objB);

        public override void Shutdown()
        {
            if (batch != null)
            {
                batch.Dispose();
                batch = null;
            }
        }


    }
}
